/**
 *	\file
 *	\brief TODO complete the documentation of lexerTest.c
 *
 * 	\date Jul 28, 2013
 * 	\author koldar
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "main-tools.h"
#include "memory-tools.h"

/**represents that this test is successful*/
#define TEST_SUCCESS 1
/**represents that this test is failed*/
#define TEST_FAILURE 0
/**represents that this test is skipped (cause is present in TESTSUITE_TESTEXCLUDED )*/
#define TEST_SKIPPED 2

/**represents the number of total tests in this suite, both included and excluded.*/
#define TESTSUITE_LENGTH 7
/**indicates a list of test IDs which won't be executed. Use commas (",") as separator*/
#define TESTSUITE_TESTEXCLUDED 0,1,2,3,5,6

#include "lexer.h"
#include "LexVal.h"
#include "lex-tools.h"
#include "parser.h"

int (*testcase[TESTSUITE_LENGTH])(void);

/**Tests if yylex() can be effectevely used
 *
 * @return true if it works, FALSE or Segmenetation fault if fails
 */
bool test00(){
	FILE* f=fopen("yaoola_src/lexer01.yaoola","r");
	int result;

	if (f==NULL){
		return false;
	}
	printf("yyin=file\n");
	yyin=f;
	printf("reading symbol...");
	result=yylex();
	printf("symbol read (expected \"class\" 260): %d\n",result);
	fclose(f);
	return true;
}

/**tests if the lexer can read an entire correct file.
 *
 * @return
 */
bool test01(){
	FILE* f=fopen("yaoola_src/lexer01.yaoola","r");
	int result=0;
	extern LexVal lexval;

	if (f==NULL){
		return false;
	}
	printf("yyin=file\n");
	//reloop the file. In this way the text before this one doesn't affect the mark in the input file.
	yylex_destroy();
	yyrestart(f);
	yyset_in(f);
	while ((result=yylex())!=0){ //if the EOF is read, yylex returns 0
		printf("symbol: %s\t\t",yytext);
		//to improve string table format
		if (strlen(yytext)<=7){
			printf("\t");
		}
		printf("token: %3d\t",result);
		switch (result){
		case TOKEN_ID:
		case TOKEN_STRINGCONST: {
			printf("value: %s\n",lexval.sval);
			break;
		}
		case TOKEN_BOOLCONST:{
			printf("value: %s\n",lexval.ival==1?"true":"false");
			break;
		}
		case TOKEN_INTCONST:{
			printf("value: %d\n",lexval.ival);
			break;
		}
		case TOKEN_CHARCONST:{
			printf("value: \"%c\"\n",lexval.ival);
			break;
		}
		case TOKEN_ERROR: {
			printf("value: \"%s\"\n",lexval.sval);
			return false;
		}
		default: {
			printf("\n");
		}
		}
	}
	fclose(f);
	return true;
}

/**test if the lexer can understand that there is an unknown symbol
 *
 * @return
 */
bool test02(){
	FILE* f=fopen("yaoola_src/lexer02.yaoola","r");
	int result=0;
	extern LexVal lexval;
		if (f==NULL){
		return false;
	}
	printf("yyin=file\n");
	//reloop the file. In this way the text before this one doesn't affect the mark in the input file.
	yylex_destroy();
	yyrestart(f);
	yyset_in(f);
	while ((result=yylex())!=0){//if the EOF is read, yylex returns 0
		printf("symbol: %s\t\t",yytext);
		//to improve string table format
		if (strlen(yytext)<=7){
			printf("\t");
		}
		printf("token: %3d\t",result);
		switch (result){
		case TOKEN_ID:
		case TOKEN_STRINGCONST: {
			printf("value: %s\n",lexval.sval);
			break;
		}
		case TOKEN_BOOLCONST:{
			printf("value: %s\n",lexval.ival==1?"true":"false");
			break;
		}
		case TOKEN_INTCONST:{
			printf("value: %d\n",lexval.ival);
			break;
		}
		case TOKEN_CHARCONST:{
			printf("value: \"%c\"\n",lexval.ival);
			break;
		}
		case TOKEN_ERROR: {
			printf("value: \"%s\"\n",lexval.sval);
			printf("THERE IS AN ERROR DETETCED AT LINE %d!! (the correct line should be 7)\n",yylineno);
			fclose(f);
			return true;
		}
		default: {
			printf("\n");
		}
		}
	}
	return false;
}

/**tests if the lexer can handle properly comments
 *
 * @return
 */
bool test03(){
	FILE* f=fopen("yaoola_src/lexer03.yaoola","r");
	int result=0;
	extern LexVal lexval;

	if (f==NULL){
		return false;
	}
	//reloop the file. In this way the text before this one doesn't affect the mark in the input file.
	yylex_destroy();
	yyrestart(f);
	yyset_in(f);
	while ((result=yylex())!=0){//if the EOF is read, yylex returns 0
		printf("symbol: %s\t\t",yytext);
		//to improve string table format
		if (strlen(yytext)<=7){
			printf("\t");
		}
		printf("token: %3d\t",result);
		switch (result){
		case TOKEN_ID:
		case TOKEN_STRINGCONST: {
			printf("value: %s\n",lexval.sval);
			break;
		}
		case TOKEN_BOOLCONST:{
			printf("value: %s\n",lexval.ival==1?"true":"false");
			break;
		}
		case TOKEN_INTCONST:{
			printf("value: %d\n",lexval.ival);
			break;
		}
		case TOKEN_CHARCONST:{
			printf("value: \"%c\"\n",lexval.ival);
			break;
		}
		case TOKEN_ERROR: {
			printf("value: \"%s\"\n",lexval.sval);
			return false;
		}
		default: {
			printf("\n");
		}
		}
	}
	fclose(f);
	return true;
}

/**tests if there are some memory leak in the lexer
 *
 * @return
 */
bool test04(){
	FILE* f=fopen("yaoola_src/lexer01.yaoola","r");
	extern LexVal lexval;
	int token;

	if (f==NULL){
		return false;
	}
	initLibrary();
	printf("reading file...\n");
	yylex_destroy();
	yyrestart(f);
	yyset_in(f);
	while ((token=yylex())!=0){//if the EOF is read, yylex returns 0
		printf("token: %s\t",yytext);
		if (token==TOKEN_ID || token==TOKEN_STRINGCONST || token==TOKEN_ERROR){
			printf("before_free: %5d\t",getMemoryAllocated());
			freeString(lexval.sval);
			printf("after_free: %5d\t",getMemoryAllocated());
		}
		printf("\n");
	}
	return getMemoryAllocated()==0;
}


bool test05(){
	FILE* f=fopen("yaoola_src/parser03_easy.yaoola","r");
	extern LexVal lexval;
	int token;
	initLibrary();
	if (f==NULL){
		printf("can't open the file f.\n");
		return false;
	}
	yylex_destroy();
	yyrestart(f);
	yyset_in(f);
	printf("lexical symbols read:\n");
	while ((token=yylex())!=0){//if the EOF is read, yylex returns 0
		if (token>255){
			printf("%d ",token);
			printLexicalToken(stdout,token);
			if (token==TOKEN_ID){
				printf("\t%s",lexval.sval);
			}
			if (token==TOKEN_ID || token==TOKEN_STRINGCONST || token==TOKEN_ERROR){
				freeMemory(strlen(lexval.sval)+1,lexval.sval);
			}
		}else{

			printf("%03d %c",token,token);
		}
		printf("\n");
	}
	fclose(f);
	return true;
}

/**tests the ambiguity of the token +. It can refers to:
 * \li a sum for example i=a+3;
 * \li a numeric constan for example i=+3;
 *
 * The particular string might be problematic:
 * <p>a+3;
 * Lexer can interprets it in 2 way:
 * \li identifier (a) and integer constant (+3);
 * \li identifier (a), sugar (+) and integer constant (3);
 *
 * @return TRUE if there are th correct number of sum and intconst within this file
 */
bool test06(){
	FILE* f=fopen("yaoola_src/lexer_test06.yaoola","r");
	extern LexVal lexval;
	int token;
	int tokenintconstnumber=0;
	int tokensumnumber=0;
	//initLibrary() has been already launched from test04
	if (f==NULL){
		printf("can't open the file f.\n");
		return false;
	}
	yylex_destroy();
	yyrestart(f);
	yyset_in(f);
	printf("lexical symbols read:\n");
	while ((token=yylex())!=0){//if the EOF is read, yylex returns 0
		if (token=='+'){
			tokensumnumber++;
		}
		if (token>255){
			printf("%d ",token);
			printLexicalToken(stdout,token);
			switch (token){
			case TOKEN_ID:{
				printf("\t%s",lexval.sval);
				break;
			}
			case TOKEN_INTCONST:{
				tokenintconstnumber++;
				printf("\t%d",lexval.ival);
			}
			}
		}else{
			printf("%03d %c",token,token);
		}
		printf("\n");
	}
	fclose(f);
	return tokenintconstnumber==9 && tokensumnumber==6;
}

bool setUp(){
	/* ************** TESTCASE Construction ***********************/
	testcase[0]=(void*)test00;
	testcase[1]=(void*)test01;
	testcase[2]=(void*)test02;
	testcase[3]=(void*)test03;
	testcase[4]=(void*)test04;
	testcase[5]=(void*)test05;
	testcase[6]=(void*)test06;
	/* ************ OTHER ******************/
	return true;
}

bool tearDown(){
	return true;
}

int main(){
	if (setUp()==false){
		printf("SETUP FAILED.\n");
		return EXIT_FAILURE;
	}
	printf("SETUP SUCCESSFUL\n");

	int i,j;
	int excluded[]={TESTSUITE_TESTEXCLUDED};
	int testResults[TESTSUITE_LENGTH];
	int failed_tests=0;
	bool toskip;

	for(i=0;i<TESTSUITE_LENGTH;i++){
		//check if the test ID i is present in the excluded array. If it is the test must be skipped
		toskip=false;
		for (j=0;j<(sizeof(excluded)/sizeof(excluded[0]));j++){
			if(excluded[j]==i){
				toskip=true;
				break;
			}
		}
		if (toskip==true){
			testResults[i]=TEST_SKIPPED;
			continue;
		}
		printf("############# Running now test case %d ##############\n",i);
		testResults[i]=(*testcase[i])();
		if (testResults[i]==true){
			printf("Test case #%d successful.\n",i);
		}else{
			printf("Test case #%d failed.\n",i);
			failed_tests++;
		}
	}

	printf("####################################################\n");
	if (tearDown()==false){
		printf("TEAR DOWN FAILED.\n");
		return EXIT_FAILURE;
	}
	printf("TEAR DOWN SUCCESSFUL.\n");
	printf("-------------------------------------------\n");
	printf("Summary of this suite:\n");
	for (i=0;i<TESTSUITE_LENGTH;i++){
		printf("Test #%d: ",i);
		switch (testResults[i]){
		case TEST_SUCCESS: {
			printf("success");
			break;
		}
		case TEST_FAILURE: {
			printf("failure");
			break;
		}
		case TEST_SKIPPED: {
			printf("skipped");
			break;
		}
		}
		printf("\n");
	}
	printf("number of test failed: %d\n",failed_tests);
	printf("--------------------------------\n");
	printf("TEST SUITE FINISHED\n");
	return EXIT_SUCCESS;
}
